# Copyright (c) 2008-2009 Mikhail Chernov 
#
# This file is part of dcExplorer.
#
# dcExplorer is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# dcExplorer is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with dcExplorer.  If not, see <http://www.gnu.org/licenses/>.

package dcExplorer::loader::OriginalXML;
use strict;
use utf8;
use loader::Generic;
use base 'dcExplorer::loader::Generic';
use core::Fleet;
use core::AlienFleet;
use core::Unit;
use Error;
use XML::Parser::PerlSAX;
use Storable qw(dclone);

sub HandleHeader
{
	my ($self, $element) = @_;
	my ($id, $turn, $time_left, $in_turn, $name) = map {$element->{Attributes}->{$_}} qw(id turn-n turn in-turn user);
	throw Error::Simple("Not logged in") unless defined($id) && defined($turn) && defined($time_left) && defined($in_turn) && defined($name);
	throw Error::Simple("Turn is in progress, please wait") if $in_turn;
	throw Error::Simple("XML's player id differs from empire's one") if (defined($self->{Empire}->getProp('player_id')) && $id != $self->{Empire}->getProp('player_id'));
	$self->{Empire}->setProp('player_id', $id);
	$self->{Empire}->setProp('time_left', $time_left);
	$self->{Empire}->setProp('turn', $turn);
	$self->{Empire}->setProp('in_turn', $in_turn);
	$self->{Empire}->setProp('name', $name);
	$self->{Empire}->setProp($_, $element->{Attributes}->{$_}) foreach qw(m s money);
	my $player = $self->{Empire}->players->get($id);
	$player->setProp('name', $name);
	$self->{Empire}->players->set($player);
}

sub HandleThisPlayer
{
	my ($self, $element) = @_;
	throw Error::Simple("XML's player id differs from empire's one") if ($element->{Attributes}->{'player-id'} != $self->{Empire}->getProp('player_id'));
	my @player_params = qw(player_name lang sex emperor taxe tehnology native_tehnology race_id login main_per_turn second_per_turn main_taxed second_taxed money_taxed money_per_turn main_per_turn_spent second_per_turn_spent main_res food_bonus second_res m timecreated homeworldx homeworldy def_rel_lord def_rel_vassal governors_count penis_size vassal_money money_per_turn_spent no_bonus_tech paid paid_ext);
	$self->{Empire}->setProp($_, $element->{Attributes}->{join '-', split /_/, $_}) foreach @player_params;
}

sub HandleThisPlayerRace
{
	my ($self, $element) = @_;
	throw Error::Simple("XML's player's race id differs from empire's one") if ($element->{Attributes}->{'race-id'} != $self->{Empire}->getProp('race_id'));
	my @race_params = qw(race_name race_nature industry_nature unused_resource t_optimal t_delta bonus_multiply bonus_speed bonus_dr bonus_price bonus_iq bonus_build_peace bonus_build_war bonus_hp bonus_attack bonus_surface bonus_mining bonus_space_units bonus_ground_units bonus_stealth bonus_detectors zanud spec nomads longlife intell old traiders small);
	$self->{Empire}->setProp($_, $element->{Attributes}->{join '-', split /_/, $_}) foreach @race_params;
}

my $army;

sub HandleHarrison
{
	my ($self, $element) = @_;
	my ($x, $y) = map {$element->{Attributes}->{$_}} qw(x y);
	$army = $self->{Empire}->harrison($x, $y);
}

sub HandlePlanet
{
	my ($self, $element) = @_;
	my ($x, $y) = map {$element->{Attributes}->{$_}} qw(x y);
	my $planet = $self->{Empire}->planets()->get($x, $y);
	my @geo;
	@geo = map {$element->{Attributes}->{$_}} qw(o e m t s)
		if (defined($element->{Attributes}->{'s'}));
	@geo = map {$element->{Attributes}->{$_}} qw(o e m temperature surface)
		if (defined($element->{Attributes}->{'surface'}));
	if (@geo)
	{
		throw Error::Simple("Geo can't change") if (defined($planet->getProp('s')) && (join(",", @geo) ne join(",", map {$planet->getProp($_)} qw(o e m t s))));
		if (!defined($planet->getProp('s')))
		{
			$planet->setProp('o', $geo[0]);
			$planet->setProp('e', $geo[1]);
			$planet->setProp('m', $geo[2]);
			$planet->setProp('t', $geo[3]);
			$planet->setProp('s', $geo[4]);
		}
	}
	my $owner = undef;
	if (defined($element->{Attributes}->{'owner-id'}))
	{
		$owner = $element->{Attributes}->{'owner-id'};
	}
	elsif (defined($element->{Attributes}->{'population'}))
	{
		$owner = $self->{Empire}->getProp('player_id');
		$planet->setProp('jumpable', 1);
		$planet->setProp('population', $element->{Attributes}->{'population'});
		$planet->setProp('corruption', $element->{Attributes}->{'corruption'});
		$planet->setProp('hidden', $element->{Attributes}->{'hidden'});
	}
	unless (defined($owner) && defined($planet->getProp('owner_id')) && $owner == $planet->getProp('owner_id') || (!defined($owner) && !defined($planet->getProp('owner_id'))))
	{
		my $player = $self->{Empire}->players->get($owner)->getProp('name');
		if ($player)
		{
			$planet->setProp('owner_name', $player);	
		}
		$planet->setProp('owner_id', $owner);
		$planet->setProp('my', defined($owner) && $owner == $self->{Empire}->getProp('player_id'));
	}
	if (defined($element->{Attributes}->{'open'}) && $element->{Attributes}->{'open'} == 1)
	{
		$planet->setProp('jumpable', 1);
	}
	if (defined($element->{Attributes}->{'name'}))
	{
		$planet->setProp('name', $element->{Attributes}->{'name'});
	}
	$planet->setProp('age', $element->{Attributes}->{'age'});
	$self->{Empire}->planets()->set($planet);
}

sub HandleFleet
{
	my ($self, $element) = @_;
	my $fleet = dcExplorer::core::Fleet->new($self->{Empire}->fleets, map {$element->{Attributes}->{$_}} qw(x y));
	for my $prop (@dcExplorer::core::Fleet::props)
	{
		next if $prop eq 'x' || $prop eq 'y';
		my $name = $prop;
		$name =~ s/_/-/g;
		$name = 'behavior' if $name eq 'behaviour';
		$name = 'tta' if $name eq 'turns-till-arrival';
		$fleet->setProp($prop, $element->{Attributes}->{$name});
	}
	$self->{Empire}->fleets->set($fleet);
	$army = $fleet;
}

sub HandleSharedFleet
{
	my ($self, $element) = @_;
	my $fleet = dcExplorer::core::Fleet->new($self->{Empire}->shared_fleets, map {$element->{Attributes}->{$_}} qw(x y));
	for my $prop (@dcExplorer::core::Fleet::props)
	{
		next if $prop eq 'x' || $prop eq 'y';
		my $name = $prop;
		$name =~ s/_/-/g;
		$name = 'behavior' if $name eq 'behaviour';
		$name = 'tta' if $name eq 'turns-till-arrival';
		$fleet->setProp($prop, $element->{Attributes}->{$name});
	}
	$self->{Empire}->shared_fleets->set($fleet);
	$army = $fleet;
}

sub HandleAllienFleet
{
	my ($self, $element) = @_;
	if (defined($element->{Attributes}->{'player-id'}))
	{
		my ($player_id, $player_name, $race_id) = map {$element->{Attributes}->{$_}} qw(player-id player-name race-id);
		my $player = $self->{Empire}->players->get($player_id);
		$player->setProp('name', $player_name);
		$player->setProp('race_id', $race_id);
		$self->{Empire}->players->set($player);
	}
	
	my $fleet = dcExplorer::core::AlienFleet->new($self->{Empire}->alien_fleets, map {$element->{Attributes}->{$_}} qw(x y));
	for my $prop (@dcExplorer::core::AlienFleet::props)
	{
		next if $prop eq 'x' || $prop eq 'y';
		my $name = $prop;
		$name =~ s/_/-/g;
		$fleet->setProp($prop, $element->{Attributes}->{$name});
	}
	$self->{Empire}->alien_fleets->set($fleet);
	$army = $fleet;
}

my $group;

sub HandleUnit
{
	return until defined($army);
	my ($self, $element) = @_;
	my $unit = dcExplorer::core::Unit->new($army, $element->{Attributes}->{'id'});
	$unit->setProp('lvl', $element->{Attributes}->{'lvl'});
	$unit->setProp('building_id', $element->{Attributes}->{'bc'});
	$unit->setProp('hit_pointsleft', $element->{Attributes}->{'hp'});
	$unit->setProp('i_tag', $element->{Attributes}->{'i-tag'});
	$unit->setProp('unwrapped', $element->{Attributes}->{'unwrapped'});
	$army->addUnit($unit);
	if ($unit->getProp('building_id'))
	{
		my $building = $self->{Empire}->designHouse->getPrototypeByID($unit->getProp('building_id'));
		if (defined($building))
		{
			$unit->setProp('max_hitpoints', $building->getProp('hit_points'));
			$unit->setProp('name', $building->getProp('s_name'));
			$unit->setProp($_, $building->getProp($_)) foreach 
				qw(is_ground_unit is_space_ship is_transportable carrier_capacity transport_capacity fly_range fly_speed weight class carapace);
			$unit->setActions(dclone($building->getActions()));
		}
	}
	$group = $unit if ($element->{Attributes}->{'bc'} == 23);
}

sub HandleAllienShip
{
	return until defined($army);
	my ($self, $element) = @_;
	my $unit = dcExplorer::core::Unit->new($army, $element->{Attributes}->{'id'});
	$unit->setProp('building_id', $element->{Attributes}->{'class-id'});
	$unit->setProp('carapace', $element->{Attributes}->{'carapace'});
	$unit->setProp('weight', $element->{Attributes}->{'weight'});
	$army->addUnit($unit);
}

sub HandleContainer
{
	my ($self, $element) = @_;
	$group->setProp('tag', '<'.$element->{Name}.' '.join(' ', map {$_.'="'.$element->{Attributes}->{$_}.'"'} keys %{$element->{Attributes}}).' />') 
		if defined $group;
}

sub HandleConstructedUnit
{
	my ($self, $element) = @_;
	my $q = $self->{Empire}->queue(map {$army->getProp($_)} qw(x y));
	$q->addItemData({
		'id' => $element->{Attributes}->{'id'}, 
		'class' => $element->{Attributes}->{'bc'}, 
		'building_points_done' => $element->{Attributes}->{'done'},
		'i_tag' => $element->{Attributes}->{'i-tag'}
	});
}

sub HandleDipRelation
{
	my ($self, $element) = @_;
	my ($player_id, $type, $name) = map {$element->{Attributes}->{$_}} qw(player type name);
	$type = undef if defined($type) && $type == -1; 
	$self->{Empire}->setDipRelation($player_id, $type);
	my $player = $self->{Empire}->players->get($player_id);
	$player->setProp('name', $name);
	$self->{Empire}->players->set($player);
}

my $proto;

sub HandlePrototype
{
	my ($self, $element) = @_;
	my $id = $element->{Attributes}->{'building-id'};
	# TODO: Remove this dirty hack!
	return unless defined $id;
	$proto = dcExplorer::core::Prototype->new($id);
	for my $prop (@dcExplorer::core::Prototype::props)
	{
		next if $prop eq 'building_id' || $prop eq 's_name' || $prop eq 's_description';
		my $name = $prop;
		$name =~ s/_/-/g;
		$proto->setProp($prop, $element->{Attributes}->{$name});
	}
	if ($id > 1000)
	{
		$proto->setProp('s_name', $element->{Attributes}->{'name'});
		$proto->setProp('s_description', $element->{Attributes}->{'description'});
		if (!defined $proto->getProp('s_name'))
		{
			$proto->setProp('s_name', dcExplorer::core::Unit::getPartName($proto->getProp('carapace')));
			$proto->setProp('s_description', "Стандартный дизайн");
		}
	}
	else
	{
		$proto->setProp('s_name', dcExplorer::core::Unit::getBuildingName($id));
	}
	
	$self->{Empire}->designHouse->set($proto);
}

sub HandleAction
{
	my ($self, $element) = @_;
	return unless defined $proto;
	my @props = qw(action cost_main cost_second cost_money cost_pepl maxcount planet_can_be);
	my $action = {};
	for my $prop (@props)
	{
		my $name = $prop;
		$name =~ s/_/-/g;
		$action->{$prop} = $element->{Attributes}->{$name};
	}
	push @{$proto->getActions()}, $action;
}

sub HandleRequestedAction
{
	my ($self, $element) = @_;
	my $fleet = $self->{Empire}->fleets->getFleetByID($element->{Attributes}->{'fleet'});
	unless (defined $fleet)
	{
		$fleet = $self->{Empire}->shared_fleets->getFleetByID($element->{Attributes}->{'fleet'});
		throw Error::Simple("Fleet doesn't exist: ".$element->{Attributes}->{'fleet'}) unless defined $fleet;
	}
	my $unit = $fleet->getUnitByID($element->{Attributes}->{'u'});
	my ($action) = grep $_->{'action'} == $element->{Attributes}->{'act'}, @{$unit->getActions()};
	$action->{'request_id'} = $element->{Attributes}->{'id'};
}

sub start_element
{
	my ($self, $element) = @_;
	$self->{Section} = $element->{Name} if ($element->{Name} =~ /^(fleets|shared-fleets|allien-fleets|diplomacy|known-planets|actions|actions-requested)$/);
	$self->HandleHeader($element) if ($element->{Name} eq 'dc');
	$self->HandleThisPlayer($element) if ($element->{Name} eq 'this-player');
	$self->HandleThisPlayerRace($element) if ($element->{Name} eq 'this-player-race');
	$self->HandlePlanet($element) if ($element->{Name} eq 'planet');
	$self->HandleFleet($element) if ($element->{Name} eq 'fleet' && defined($self->{Section}) && $self->{Section} eq 'fleets');
	$self->HandleSharedFleet($element) if ($element->{Name} eq 'fleet' && defined($self->{Section}) && $self->{Section} eq 'shared-fleets');
	$self->HandleHarrison($element) if ($element->{Name} eq 'harrison');
	$self->HandleAllienFleet($element) if ($element->{Name} eq 'allien-fleet');
	$self->HandleUnit($element) if ($element->{Name} eq 'u');
	$self->HandleConstructedUnit($element) if ($element->{Name} eq 'c-u');
	$self->HandleAllienShip($element) if ($element->{Name} eq 'allien-ship');
	$self->HandleContainer($element) if ($element->{Name} =~ /^(part|itm-in-pack|res-in-pack)$/);
	$self->HandleDipRelation($element) if ($element->{Name} eq 'rel' && defined($self->{Section}) && $self->{Section} eq 'diplomacy');
	$self->HandlePrototype($element) if ($element->{Name} eq 'building_class' || $element->{Name} eq 'bc');
	$self->HandleAction($element) if ($element->{Name} eq 'act' && $self->{Section} eq 'actions');
	$self->HandleRequestedAction($element) if ($element->{Name} eq 'act' && $self->{Section} eq 'actions-requested');
}

sub end_element
{
	my ($self, $element) = @_;
	$army = undef if ($element->{Name} =~ /^(harrison|fleet|allien-fleet)$/);
	$group = undef if ($element->{Name} eq 'u' && defined $group);
	$proto = undef if ($element->{Name} eq 'building_class' && defined $proto);
	$self->{Section} = undef if (defined($self->{Section}) && $element->{Name} eq $self->{Section});
}

sub loadXML
{
	my $self = shift;
	my $xml = shift;
	my $parser = XML::Parser::PerlSAX->new(Handler => $self);
	$parser->parse($xml);
}

1;
